package com.copyctrl.campus.portal.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.copyctrl.campus.common.constant.RedisConstant;
import com.copyctrl.campus.common.constant.RedisKind;
import com.copyctrl.campus.common.domain.*;
import com.copyctrl.campus.portal.dto.DynamicDTO;
import com.copyctrl.campus.portal.service.CommentService;
import com.copyctrl.campus.portal.service.DynamicService;
import com.copyctrl.campus.portal.util.RedisUtil;
import com.copyctrl.mapper.CampDynamicMapper;
import com.copyctrl.model.CampDynamic;
import com.copyctrl.model.CampDynamicExample;
import com.copyctrl.publisher.send.DataPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author:JH
 * @version:1.0 TODO 待完善,feed流拉模式，动态推送
 */
@Service
@Slf4j
public class DynamicServiceImpl implements DynamicService {
    @Autowired
    CampDynamicMapper mapper;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    CommentService commentService;

    @Autowired
    DataPublisher dataPublisher;

    /**
     * @param dynamic
     * @return
     */
    @Override
    public int addDynamic(CampDynamic dynamic) {
        dynamic.setCreateTime(new Date());
        dynamic.setLikes(0);
        dynamic.setComments(0);
        int insert = mapper.insert(dynamic);
        if (insert == 1) {
            DynamicDTO dynamicRedis = new DynamicDTO();
            BeanUtil.copyProperties(dynamic, dynamicRedis, true);
            String[] pics = StrUtil.removeAll(dynamic.getPic(), '[', ']').split(",");
            pics = pics.length == 0 ? new String[]{} : Arrays.stream(pics).map(p -> p.replace("\"", "")).toArray(String[]::new);
            dynamicRedis.setPic(pics);
            RedisData data = RedisData.getRedisData(String.class, RedisKind.STRING, JSONUtil.toJsonStr(dynamicRedis), "set", RedisConstant.DYNAMIC_KEY + dynamic.getId());
            SendData send = new SendData();
            send.setDataType(true);
            send.setRedisData(data);
            dataPublisher.redisData(JSONUtil.toJsonStr(send));
            //给所有粉丝推送动态id(异步多线程)
            executePublish(dynamic.getUserId(), dynamic);
        }
        return insert;
    }

    /**
     * @param dynamic
     * @return
     */
    @Override
    public int updateDynamic(CampDynamic dynamic) {
        CampDynamicExample example = new CampDynamicExample();
        example.createCriteria().andIdEqualTo(dynamic.getId());
        int i = mapper.updateByExampleSelective(dynamic, example);
        if (i == 1) {
            DynamicDTO dynamicRedis = new DynamicDTO();
            BeanUtil.copyProperties(dynamic, dynamicRedis, true);
            String[] pics = StrUtil.removeAll(dynamic.getPic(), '[', ']').split(",");
            pics = pics.length == 0 ? new String[]{} : Arrays.stream(pics).map(p -> p.replace("\"", "")).toArray(String[]::new);
            dynamicRedis.setPic(pics);
            RedisData data = RedisData.getRedisData(String.class, RedisKind.STRING, JSONUtil.toJsonStr(dynamicRedis), "set", RedisConstant.DYNAMIC_KEY + dynamic.getId());
            SendData send = new SendData();
            send.setDataType(true);
            send.setRedisData(data);
            dataPublisher.redisData(JSONUtil.toJsonStr(send));
        }
        return i;
    }

    /**
     * 删除动态，异步删除mysql中的数据，异步删除redis中的评论，异步删除MySQL中的评论。
     * TODO 对于异步线程是否可以开启事务，如果异步方法出现异常，调用异步方法的方法是否可以回滚？
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteDynamic(Integer id) {
        int i = mapper.deleteByPrimaryKey(id);
        if (i == 1) {
            //做异步
            execute(id);
        }
        return i;
    }

    /**
     * 获取自己/好友 的所有动态，待完善，同样开启分页
     *
     * @param userId
     * @return
     */
    @Override
    public ScrollResult<DynamicDTO> getDynamics(Integer userId, Integer offset, Long max,Integer userId2) {
        ScrollResult scrollResult = RedisUtil.getDynamicByPage(redisTemplate, RedisConstant.DYNAMIC_USER_WRITE + userId, max, offset, 10);
        List<DynamicDTO> list = new ArrayList<>();
        List<String> resultList = scrollResult.getList();
        if (resultList != null) {
            for (String o : resultList) {
                String json = redisTemplate.opsForValue().get(RedisConstant.DYNAMIC_KEY + o);
                DynamicDTO dynamicDTO = JSONUtil.toBean(json, DynamicDTO.class);
                Integer id = dynamicDTO.getUserId();
                if (id == null) {
                    redisTemplate.opsForZSet().remove(RedisConstant.DYNAMIC_USER_WRITE + userId, o);
                    continue;
                }
                User user = getUserById(id.toString());
                dynamicDTO.setUser(user);
                list.add(dynamicDTO);
            }
            //从redis中取出点赞动态列表
            Set<String> likes = redisTemplate.opsForSet().members(RedisConstant.USER_LIKED_IDS + userId2);
            if (likes.size() > 0) {
                list = list.stream()
                        .map(dynamicDTO -> {
                            if (likes.contains(dynamicDTO.getId().toString())) {
                                dynamicDTO.setLiked(1);
                            } else {
                                dynamicDTO.setLiked(0);
                            }
                            return dynamicDTO;
                        }).collect(Collectors.toList());
            } else {
                list = list.stream().map(dynamicDTO -> {
                    dynamicDTO.setLiked(0);
                    return dynamicDTO;
                }).collect(Collectors.toList());
            }
            scrollResult.setList(list);
        }
        return scrollResult;
    }

    /**
     * 获取关注好友的动态 用stream流进行按时间排序，如果想分页查询，就必须给每个用户开通一个“邮箱” 用于监听关注的人发布的动态，采用邮箱
     * 就要在发布动态时做工作，会占用一定的内存。
     * 如果不使用邮箱的话，每次点开“朋友圈”就会一次性加载出所有好友动态，数据多了会影响用户的体验
     *
     * @param userId
     * @return
     */
    @Override
    public ScrollResult<DynamicDTO> getAllDynamics(Integer userId, Integer offset, Long score) {
        ScrollResult scrollResult = RedisUtil.getDynamicByPage(redisTemplate, RedisConstant.DYNAMIC_USER_SAVE + userId, score, offset, 10);
        List<DynamicDTO> list = new ArrayList<>();
        List<String> resultList = scrollResult.getList();
        if (resultList != null) {
            for (String o : resultList) {
                String json = redisTemplate.opsForValue().get(RedisConstant.DYNAMIC_KEY + o);
                DynamicDTO dynamicDTO = JSONUtil.toBean(json, DynamicDTO.class);
                Integer id = dynamicDTO.getUserId();
                if (id == null) {
                    redisTemplate.opsForZSet().remove(RedisConstant.DYNAMIC_USER_SAVE + userId, o);
                    continue;
                }
                User user = getUserById(id.toString());
                dynamicDTO.setUser(user);
                list.add(dynamicDTO);
            }
            //从redis中取出点赞动态列表
            Set<String> likes = redisTemplate.opsForSet().members(RedisConstant.USER_LIKED_IDS + userId);
            if (likes.size() > 0) {
                list = list.stream()
                        .map(dynamicDTO -> {
                            if (likes.contains(dynamicDTO.getId().toString())) {
                                dynamicDTO.setLiked(1);
                            } else {
                                dynamicDTO.setLiked(0);
                            }
                            return dynamicDTO;
                        }).collect(Collectors.toList());
            } else {
                list = list.stream().map(dynamicDTO -> {
                    dynamicDTO.setLiked(0);
                    return dynamicDTO;
                }).collect(Collectors.toList());
            }
            scrollResult.setList(list);
        }
        return scrollResult;
    }

    @Override
    public Long liked(String userId, String dynamicId) {
        Long add = redisTemplate.opsForSet().add(RedisConstant.USER_LIKED_IDS + userId, dynamicId);
        if (add != null && add == 1) {
            //异步修改mysql，redis中的点赞数量
            //直接执行updateDynamic
            String json = redisTemplate.opsForValue().get(RedisConstant.DYNAMIC_KEY + dynamicId);
            DynamicDTO dynamicDTO = JSONUtil.toBean(json, DynamicDTO.class);
            StringBuffer pic = new StringBuffer("[");
            String[] strings = dynamicDTO.getPic();
            int length = strings.length;
            for (int i = 0; i < length; i++) {
                if (i != length) {
                    pic.append(strings[i] + ",");
                } else {
                    pic.append(strings[i]);
                }
            }
            pic.append("]");
            CampDynamic dynamic = BeanUtil.copyProperties(dynamicDTO, CampDynamic.class);
            dynamic.setPic(pic.toString());
            dynamic.setLikes(dynamic.getLikes() + 1);
            updateDynamic(dynamic);
        }
        return add;
    }

    @Override
    public Long cancel(String userId, String dynamicId) {
        Long remove = redisTemplate.opsForSet().remove(RedisConstant.USER_LIKED_IDS + userId, dynamicId);
        if (remove != null && remove == 1) {
            //异步修改mysql，redis中的点赞数量
            //直接执行updateDynamic
            String json = redisTemplate.opsForValue().get(RedisConstant.DYNAMIC_KEY + dynamicId);
            DynamicDTO dynamicDTO = JSONUtil.toBean(json, DynamicDTO.class);
            StringBuffer pic = new StringBuffer("[");
            String[] strings = dynamicDTO.getPic();
            int length = strings.length;
            for (int i = 0; i < length; i++) {
                if (i != length) {
                    pic.append(strings[i] + ",");
                } else {
                    pic.append(strings[i]);
                }
            }
            pic.append("]");
            CampDynamic dynamic = BeanUtil.copyProperties(dynamicDTO, CampDynamic.class);
            dynamic.setPic(pic.toString());
            dynamic.setLikes(dynamic.getLikes() - 1);
            updateDynamic(dynamic);
        }
        return remove;
    }

    @Override
    public int updateCommentsNumber(Integer dId,Integer num) {
        CampDynamicExample example = new CampDynamicExample();
        example.createCriteria().andIdEqualTo(dId);
        CampDynamic dynamic=new CampDynamic();
        dynamic.setComments(num);
        dynamic.setId(dId);
        int i = mapper.updateByExampleSelective(dynamic, example);
        if (i == 1) {
            String json = redisTemplate.opsForValue().get(RedisConstant.DYNAMIC_KEY + dId);
            DynamicDTO dynamicDTO = JSONUtil.toBean(json, DynamicDTO.class);
            dynamicDTO.setComments(num);
            redisTemplate.opsForValue().set(RedisConstant.DYNAMIC_KEY + dId,JSONUtil.toJsonStr(dynamicDTO));
        }
        return i;
    }


    /**
     * 异步删除评论
     *
     * @param id 处理动态的id，拼接redis的key值,删除redis收件箱中的id，这个不好删除，应该在用户取动态时，获取到null时随手删除
     */
    @Async("defaultThreadPoolExecutor")
    public void execute(Integer id) {
        redisTemplate.delete(RedisConstant.DYNAMIC_KEY + id);
        commentService.deleteByTextId(id, 1);
    }

    /**
     * 把动态id推送到关注者的收件箱
     *
     * @param dynamic
     * @PARAM userId
     */
    @Async("defaultThreadPoolExecutor")
    public void executePublish(Integer userId, CampDynamic dynamic) {
        //取出所有粉丝id，循环把动态id插入收件箱
        Set<String> members = redisTemplate.opsForSet().members(RedisConstant.USER_IS_FOLLOWED + userId);
        for (String member : members) {
            String key = RedisConstant.DYNAMIC_USER_SAVE + member;
            redisTemplate.opsForZSet().add(key, dynamic.getId().toString(), dynamic.getCreateTime().getTime());
        }
        //把文章id存入redis中用户自己动态的kv中
        redisTemplate.opsForZSet().add(RedisConstant.DYNAMIC_USER_WRITE + userId, dynamic.getId().toString(), dynamic.getCreateTime().getTime());

    }

    public User getUserById(String userId) {
        Object openid = redisTemplate.opsForHash().get(RedisConstant.ID_OPENID, userId.toString());
        String json = redisTemplate.opsForValue().get(RedisConstant.USER_KEY + openid);
        User user = JSONUtil.toBean(json, User.class);
        return user;
    }

}
